/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include "DAP_config.h"
#include "DAP.h"

// SW Macros

#define PIN_SWCLK_SET PIN_SWCLK_TCK_SET
#define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR

#define SW_CLOCK_CYCLE() \
    PIN_SWCLK_CLR();     \
    PIN_DELAY();         \
    PIN_SWCLK_SET();     \
    PIN_DELAY()

#define SW_WRITE_BIT(bit) \
    PIN_SWDIO_OUT(bit);   \
    PIN_SWCLK_CLR();      \
    PIN_DELAY();          \
    PIN_SWCLK_SET();      \
    PIN_DELAY()

#define SW_READ_BIT(bit)  \
    PIN_SWCLK_CLR();      \
    PIN_DELAY();          \
    bit = PIN_SWDIO_IN(); \
    PIN_SWCLK_SET();      \
    PIN_DELAY()

#define PIN_DELAY() PIN_DELAY_FAST()

// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence(uint16_t count, const uint8_t* data)
{
    uint8_t val;
    uint8_t n;

    val = 0U;
    n = 0U;
    while (count--) {
        if (n == 0U) {
            val = *data++;
            n = 8U;
        }
        if (val & 1U) {
            PIN_SWDIO_TMS_SET();
        } else {
            PIN_SWDIO_TMS_CLR();
        }
        SW_CLOCK_CYCLE();
        val >>= 1;
        n--;
    }
}
#endif

// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
#if (DAP_SWD != 0)
void SWD_Sequence(uint8_t info, const uint8_t* swdo, uint8_t* swdi)
{
    uint8_t val;
    uint8_t bit;
    uint8_t n, k;

    n = info & SWD_SEQUENCE_CLK;
    if (n == 0U) {
        n = 64U;
    }

    if (info & SWD_SEQUENCE_DIN) {
        while (n) {
            val = 0U;
            for (k = 8U; k && n; k--, n--) {
                SW_READ_BIT(bit);
                val >>= 1;
                val |= bit << 7;
            }
            val >>= k;
            *swdi++ = (uint8_t)val;
        }
    } else {
        while (n) {
            val = *swdo++;
            for (k = 8U; k && n; k--, n--) {
                SW_WRITE_BIT(val);
                val >>= 1;
            }
        }
    }
}

// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
uint8_t SWD_Transfer(uint8_t request, uint8_t* data)
{
    uint8_t ack;
    uint8_t bit;
    uint8_t val;
    uint8_t parity;

    /* Packet Request */
    parity = 0U;
    SW_WRITE_BIT(1U); /* Start Bit */
    bit = request >> 0;
    SW_WRITE_BIT(bit); /* APnDP Bit */
    parity += bit;
    bit = request >> 1;
    SW_WRITE_BIT(bit); /* RnW Bit */
    parity += bit;
    bit = request >> 2;
    SW_WRITE_BIT(bit); /* A2 Bit */
    parity += bit;
    bit = request >> 3;
    SW_WRITE_BIT(bit); /* A3 Bit */
    parity += bit;
    SW_WRITE_BIT(parity); /* Parity Bit */
    SW_WRITE_BIT(0U); /* Stop Bit */
    SW_WRITE_BIT(1U); /* Park Bit */

    /* Turnaround */
    PIN_SWDIO_OUT_DISABLE();
    for (uint8_t n = DAP_Data.swd_conf.turnaround; n; n--) {
        SW_CLOCK_CYCLE();
    }

    /* Acknowledge response */
    SW_READ_BIT(bit);
    ack = bit << 0;
    SW_READ_BIT(bit);
    ack |= bit << 1;
    SW_READ_BIT(bit);
    ack |= bit << 2;

    if (ack == DAP_TRANSFER_OK) { /* OK response */
        /* Data transfer */
        if (request & DAP_TRANSFER_RnW) {
            /* Read data */
            val = 0U;
            parity = 0U;
            for (uint8_t b = 0; b < 4; b++) {
                for (uint8_t n = 8; n; n--) {
                    SW_READ_BIT(bit); /* Read RDATA[0:31] */
                    parity += bit;
                    if (data) {
                        data[b] >>= 1;
                        data[b] |= bit << 7;
                    }
                }
            }
            SW_READ_BIT(bit); /* Read Parity */
            if ((parity ^ bit) & 1U) {
                ack = DAP_TRANSFER_ERROR;
            }
            /* Turnaround */
            for (uint8_t n = DAP_Data.swd_conf.turnaround; n; n--) {
                SW_CLOCK_CYCLE();
            }
            PIN_SWDIO_OUT_ENABLE();
        } else {
            /* Turnaround */
            for (uint8_t n = DAP_Data.swd_conf.turnaround; n; n--) {
                SW_CLOCK_CYCLE();
            }
            PIN_SWDIO_OUT_ENABLE();
            /* Write data */
            parity = 0U;
            for (uint8_t b = 0; b < 4; b++) {
                val = *(data + b);
                for (uint8_t n = 8U; n; n--) {
                    SW_WRITE_BIT(val); /* Write WDATA[0:31] */
                    parity += val;
                    val >>= 1;
                }
            }
            SW_WRITE_BIT(parity); /* Write Parity Bit */
        }
        /* Capture Timestamp */
        if (request & DAP_TRANSFER_TIMESTAMP) {
            DAP_Data.timestamp = TIMESTAMP_GET();
        }
        /* Idle cycles */
        if (DAP_Data.transfer.idle_cycles) {
            PIN_SWDIO_OUT(0U);
            for (uint8_t cycles = DAP_Data.transfer.idle_cycles; cycles; cycles--) {
                SW_CLOCK_CYCLE();
            }
        }
        PIN_SWDIO_OUT(1U);
        return ((uint8_t)ack);
    }

    if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
        /* WAIT or FAULT response */
        if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) {
            for (uint8_t n = 32U + 1U; n; n--) {
                SW_CLOCK_CYCLE(); /* Dummy Read RDATA[0:31] + Parity */
            }
        }
        /* Turnaround */
        for (uint8_t n = DAP_Data.swd_conf.turnaround; n; n--) {
            SW_CLOCK_CYCLE();
        }
        PIN_SWDIO_OUT_ENABLE();
        if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
            PIN_SWDIO_OUT(0U);
            for (uint8_t n = 32U + 1U; n; n--) {
                SW_CLOCK_CYCLE(); /* Dummy Write WDATA[0:31] + Parity */
            }
        }
        PIN_SWDIO_OUT(1U);
        return ((uint8_t)ack);
    }

    /* Protocol error */
    for (uint8_t n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--) {
        SW_CLOCK_CYCLE(); /* Back off data phase */
    }
    PIN_SWDIO_OUT_ENABLE();
    PIN_SWDIO_OUT(1U);
    return ((uint8_t)ack);
}

#endif /* (DAP_SWD != 0) */
